"""
GCR-specific database adapter for the unified log database manager.

This adapter provides the same interface as the original SimpleDatabase
but uses the new unified LogDatabaseManager internally.
"""

import logging
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Any

from log_database_manager import LogDatabaseManager

logger = logging.getLogger(__name__)


class GCRDatabaseAdapter:
    """Adapter to provide SimpleDatabase-compatible interface using LogDatabaseManager."""
    
    def __init__(self, db_path: str = "gcr_logs.db"):
        """Initialize adapter with the unified database manager."""
        self.db_manager = LogDatabaseManager(
            db_path=db_path,
            logs_dir="logs"
        )
        self.db_path = Path(db_path)
        logger.info(f"GCR Database adapter initialized at {self.db_path}")
    
    def add_build(self, solver: str, gpu: Optional[str], config: Dict[str, Any], 
                  log_content: str, tags: Optional[List[str]] = None) -> int:
        """Record a build run with log content."""
        return self.db_manager.add_run(
            run_type="build",
            name=solver,
            category=gpu,
            config=config,
            log_content=log_content,
            tags=tags or [],
            status=self._extract_build_status(log_content)
        )
    
    def add_test(self, solver: str, gpu: Optional[str], params: Dict[str, Any], 
                 log_content: str, tags: Optional[List[str]] = None) -> int:
        """Record a test run with extracted norms and results."""
        # Extract norms and test results
        norms = self._extract_norms(log_content)
        status = self._extract_test_status(log_content)
        
        results = {
            'norms': norms,
            'converged': status == 'success',
            'error_info': self._extract_errors(log_content),
            'exit_code': 0 if status == 'success' else 1
        }
        
        return self.db_manager.add_run(
            run_type="test",
            name=solver,
            category=gpu,
            config=params,
            results=results,
            log_content=log_content,
            tags=tags or [],
            status=status
        )
    
    def get_latest(self, n: int = 10, run_type: Optional[str] = None, 
                   solver: Optional[str] = None, gpu: Optional[str] = None) -> List[Dict[str, Any]]:
        """Get latest runs with optional filters."""
        entries = self.db_manager.get_runs(
            limit=n,
            run_type=run_type,
            name=solver,
            category=gpu
        )
        
        # Convert to the format expected by the old interface
        results = []
        for entry in entries:
            result = {
                'id': entry.id,
                'type': entry.run_type,
                'solver': entry.name,
                'gpu': entry.category,
                'config': entry.config,
                'results': entry.results,
                'status': entry.status,
                'log_path': entry.log_path,
                'created_at': entry.start_time.strftime('%Y-%m-%d %H:%M:%S') if entry.start_time else None,
                'tags': entry.tags
            }
            results.append(result)
        
        return results
    
    def get_by_id(self, run_id: int) -> Optional[Dict[str, Any]]:
        """Get a specific run by ID."""
        entry = self.db_manager.get_run(run_id)
        if not entry:
            return None
        
        return {
            'id': entry.id,
            'type': entry.run_type,
            'solver': entry.name,
            'gpu': entry.category,
            'config': entry.config,
            'results': entry.results,
            'status': entry.status,
            'log_path': entry.log_path,
            'created_at': entry.start_time.strftime('%Y-%m-%d %H:%M:%S') if entry.start_time else None,
            'tags': entry.tags
        }
    
    def compare_norms(self, id1: int, id2: int) -> Optional[Dict[str, Any]]:
        """Compare norms between two test runs."""
        comparison = self.db_manager.compare_runs(id1, id2)
        if not comparison:
            return None
        
        # Extract norms from results
        run1_results = comparison['run1']['results']
        run2_results = comparison['run2']['results']
        
        norms1 = run1_results.get('norms', {}) if run1_results else {}
        norms2 = run2_results.get('norms', {}) if run2_results else {}
        
        # Compare common norms
        norm_comparison = {}
        common_keys = set(norms1.keys()) & set(norms2.keys())
        
        for key in common_keys:
            val1, val2 = norms1[key], norms2[key]
            diff = abs(val1 - val2)
            max_val = max(abs(val1), abs(val2), 1e-15)
            diff_pct = (diff / max_val) * 100
            
            norm_comparison[key] = {
                'run1': val1,
                'run2': val2,
                'difference': diff,
                'difference_percent': diff_pct,
                'relative_error': diff / max_val
            }
        
        return {
            'run1_id': id1,
            'run2_id': id2,
            'comparisons': norm_comparison,
            'summary': {
                'total_norms': len(common_keys),
                'max_difference_percent': max([c['difference_percent'] for c in norm_comparison.values()]) if norm_comparison else 0,
                'avg_difference_percent': sum([c['difference_percent'] for c in norm_comparison.values()]) / len(norm_comparison) if norm_comparison else 0
            }
        }
    
    def search_runs(self, query: str, run_type: Optional[str] = None) -> List[Dict[str, Any]]:
        """Search runs by text query."""
        entries = self.db_manager.search_runs(query, run_type)
        
        results = []
        for entry in entries:
            result = {
                'id': entry.id,
                'type': entry.run_type,
                'solver': entry.name,
                'gpu': entry.category,
                'config': entry.config,
                'results': entry.results,
                'status': entry.status,
                'log_path': entry.log_path,
                'created_at': entry.start_time.strftime('%Y-%m-%d %H:%M:%S') if entry.start_time else None,
                'tags': entry.tags
            }
            results.append(result)
        
        return results
    
    def get_statistics(self) -> Dict[str, Any]:
        """Get database statistics."""
        stats = self.db_manager.get_statistics()
        
        # Convert to old format
        overview = stats['overview']
        build_runs = sum(rt['count'] for rt in stats['run_types'] if rt['run_type'] == 'build')
        test_runs = sum(rt['count'] for rt in stats['run_types'] if rt['run_type'] == 'test')
        
        # Convert solver stats from name field
        solver_stats = []
        solver_counts = {}
        
        # We'd need to query the database directly for solver breakdown
        # since the new system uses 'name' field for solvers
        entries = self.db_manager.get_runs(limit=1000)  # Get recent entries for stats
        for entry in entries:
            solver = entry.name
            solver_counts[solver] = solver_counts.get(solver, 0) + 1
        
        solver_stats = [{'solver': k, 'count': v} for k, v in 
                       sorted(solver_counts.items(), key=lambda x: x[1], reverse=True)]
        
        return {
            'overview': {
                'total_runs': overview['total_runs'],
                'build_runs': build_runs,
                'test_runs': test_runs,
                'successful_runs': overview['successful_runs'],
                'failed_runs': overview['failed_runs']
            },
            'solvers': solver_stats,
            'recent_activity': stats['recent_activity'],
            'database_path': str(self.db_path)
        }
    
    def cleanup_old_runs(self, days: int = 90) -> int:
        """Remove runs older than specified days."""
        return self.db_manager.cleanup_old_runs(days)
    
    def _extract_build_status(self, content: str) -> str:
        """Extract build status from log content."""
        content_lower = content.lower()
        if "build failed" in content_lower or "error:" in content_lower:
            return "failed"
        elif "build succeeded" in content_lower or "finished" in content_lower:
            return "success"
        else:
            return "running"
    
    def _extract_test_status(self, content: str) -> str:
        """Extract test completion status from log content."""
        content_lower = content.lower()
        if "converged" in content_lower and "failed" not in content_lower:
            return "success"
        elif "failed" in content_lower or "error" in content_lower:
            return "failed"
        else:
            return "running"
    
    def _extract_norms(self, content: str) -> Dict[str, float]:
        """Extract norm values from solver log content."""
        norms = {}
        
        for line in content.splitlines():
            line = line.strip()
            if not line:
                continue
                
            # Parse lines with norm information
            # Examples: "residual_norm = 1.234e-5", "Final norm: 2.345e-8"
            if '=' in line and ('norm' in line.lower() or 'residual' in line.lower()):
                parts = line.split('=')
                if len(parts) == 2:
                    key = parts[0].strip().split()[-1]  # Get last word before =
                    try:
                        value = float(parts[1].strip().split()[0])  # Get first word after =
                        norms[key] = value
                    except (ValueError, IndexError):
                        continue
            
            # Parse colon-separated format: "Final norm: 1.234e-5"
            elif ':' in line and ('norm' in line.lower() or 'residual' in line.lower()):
                parts = line.split(':')
                if len(parts) == 2:
                    key = parts[0].strip().lower().replace(' ', '_')
                    try:
                        value = float(parts[1].strip().split()[0])
                        norms[key] = value
                    except (ValueError, IndexError):
                        continue
        
        return norms
    
    def _extract_errors(self, content: str) -> List[str]:
        """Extract error messages from log content."""
        errors = []
        for line in content.splitlines():
            line = line.strip()
            if 'error' in line.lower() or 'failed' in line.lower():
                errors.append(line)
        return errors[:5]  # Limit to first 5 errors


# For backward compatibility, alias the adapter as SimpleDatabase
SimpleDatabase = GCRDatabaseAdapter